/*
 *    Copyright (c) 2018-2025, proj All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the proj developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: proj
 */
package com.ykht.proj.activity.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ykht.proj.activity.Enum.PayStatusEnum;
import com.ykht.proj.activity.dto.CnActivityOrderDTO;
import com.ykht.proj.activity.dto.CnActivityOrderExportReq;
import com.ykht.proj.activity.entity.*;
import com.ykht.proj.activity.mapper.CnActivityMapper;
import com.ykht.proj.activity.mapper.CnActivityOrderMapper;
import com.ykht.proj.activity.mapper.CnActivityShopMapper;
import com.ykht.proj.activity.mq.producer.ScheduledMsgProducer;
import com.ykht.proj.activity.service.CnActivityOrderService;
import com.ykht.proj.activity.service.CnActivityPersonShopService;
import com.ykht.proj.activity.service.CnActivityShopService;
import com.ykht.proj.activity.utils.wx.WxAPIV3AesUtil;
import com.ykht.proj.activity.utils.wx.WxPayProperties;
import com.ykht.proj.activity.utils.wx.WxPayutil;
import com.ykht.proj.activity.vo.AboutActivityShopVo;
import com.ykht.proj.activity.vo.ActivityOrderVo;
import com.ykht.proj.common.core.constant.CommonConstants;
import com.ykht.proj.common.core.util.R;
import com.ykht.proj.common.security.util.SecurityUtils;
import com.ykht.proj.coupon.api.feign.RemotePersonService;
import com.ykht.proj.coupon.entity.CnPerson;
import com.ykht.proj.coupon.vo.AboutShopVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.io.*;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 活动订单表
 *
 * @author csg
 * @date 2020-12-23 11:13:32
 */
@Service
@Slf4j
public class CnActivityOrderServiceImpl extends ServiceImpl<CnActivityOrderMapper, CnActivityOrder> implements CnActivityOrderService {
	@Resource
	private CnActivityOrderMapper cnActivityOrderMapper;
	@Resource
	private CnActivityMapper cnActivityMapper;
	@Resource
	private CnActivityPersonShopService cnActivityPersonShopService;
	@Resource
	private CnActivityShopMapper cnActivityShopMapper;

	/**
	 * 订单列表（分页，搜索）
	 *
	 * @param page
	 * @param req
	 * @return
	 */
	@Override
	public IPage<CnActivityOrderDTO> getCnActivityOrderListPage(Page page, CnActivityOrderDTO req) {
		return cnActivityOrderMapper.getCnActivityOrderListPage(page, req);
	}


	/**
	 * 结算生成订单
	 * @param activityId
	 * @param shopIds
	 * @return
	 */
	@Override
	@Transactional
	public R order(Long activityId, String shopIds) {

		try {
			Long loginId = SecurityUtils.getPersonId();

			R<CnPerson> r = remotePersonService.getPersonById(loginId);
			CnPerson person = r.getData();
			if (null==person.getPhone()){
				return R.failed("用户未授权手机号");
			}

			CnActivity act = cnActivityMapper.selectById(activityId);
			List<Long> ids = Arrays.stream(shopIds.split(",")).map(Long::parseLong).collect(Collectors.toList());
			if (act.getShopNumber() != ids.size()) {
				return R.failed(String.format("应当选择%s个商家，实际选择了%s个商家", act.getShopNumber(), ids.size()));
			}
			//校验门店id是不是该任务里的，防止非法调用
			List<Long> shopList = cnActivityShopMapper.selectList(Wrappers.lambdaQuery(CnActivityShop.class).eq(CnActivityShop::getActivityId, activityId))
					.stream().map(o -> o.getShopId()).collect(Collectors.toList());
			List falseList = ids.stream().filter(o -> shopList.contains(o) == false).collect(Collectors.toList());
			log.info("门店{}，错误门店：{}",shopList,falseList);

			if (falseList.size() > 0) return R.failed("存在非法门店id");
			//创建订单
			CnActivityOrder order = new CnActivityOrder();
			BeanUtil.copyProperties(act, order);
			order.setId(null);
			order.setActivityId(act.getId());
			order.setCreateUser(loginId);
			order.setCreateTime(LocalDateTime.now());
			order.setActivityName(act.getName());
			order.setActivityLabelName(act.getActivityLabelName());
			order.setUpdateTime(null);
			order.setUpdateUser(null);
			order.setTenantId(null);
			//支付状态 0 待支付 1已完成 2已过期
			order.setPayStatus(PayStatusEnum.NOT_PAY.getValue());
			order.setPersonId(loginId);
			//订单号
			String orderNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))+RandomUtil.randomNumbers(4);
			order.setOrderNumber(orderNumber);
			this.save(order);
			//保存该用户选择的门店
			List<CnActivityPersonShop> tempList = ids.stream().map(shopId -> {
				CnActivityPersonShop temp = new CnActivityPersonShop();
				temp.setCreateTime(LocalDateTime.now());
				temp.setCreateUser(loginId);
				temp.setOrderId(order.getId());
				temp.setPerId(loginId);
				temp.setShopId(shopId);
				return temp;
			}).collect(Collectors.toList());
			cnActivityPersonShopService.saveBatch(tempList, 100);

			log.info("->生产者发送消息");
			// 设置延时等级16,这个消息将在30分钟之后发送(现在只支持固定的几个时间,详看delayTimeLevel)1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
			scheduledMsgProducer.sendOrderScheduledMsg(order.getId().toString(),16);
			return R.ok(order.getId(),"下单成功");
		} catch (Exception e) {
			e.printStackTrace();
			return R.failed("下单失败");
		}

	}
	@Resource
	private ScheduledMsgProducer scheduledMsgProducer;

	/**
	 * 下载订单结算数据
	 *
	 * @param req
	 */
	@Override
	public void downloadExcel(CnActivityOrderDTO req) {
		// 获取满足条件的导出数据
		Page<CnActivityOrder> page = new Page();
		page.setSize(999999);
		IPage<CnActivityOrderDTO> orderDTOIPage = cnActivityOrderMapper.getCnActivityOrderListPage(page, req);
		List<CnActivityOrderDTO> dtoList = orderDTOIPage.getRecords();
		List<CnActivityOrderExportReq> bean = new ArrayList<>();
		// 日期转换
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		dtoList.stream().forEach(itme -> {
			if (PayStatusEnum.NOT_PAY.getValue().equals(itme.getPayStatus())) {
				itme.setPayStatusShow(PayStatusEnum.NOT_PAY.getDesc());
			} else if (PayStatusEnum.PAY.getValue().equals(itme.getPayStatus())) {
				itme.setPayStatusShow(PayStatusEnum.PAY.getDesc());
			} else if (PayStatusEnum.BE_OVERDUE.getValue().equals(itme.getPayStatus())) {
				itme.setPayStatusShow(PayStatusEnum.BE_OVERDUE.getDesc());
			}
			if (null != itme.getPayTime()) {
				itme.setPayTimeShow(dateTimeFormatter.format(itme.getPayTime()));
			}
			bean.add(BeanUtil.toBean(itme, CnActivityOrderExportReq.class));
		});
		ExcelWriter writer = null;
		ServletOutputStream out = null;
		HttpServletResponse response = null;
		try {
			writer = ExcelUtil.getWriter(true);
			response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
			//自定义标题名
			writer.addHeaderAlias("id", "排序");
			writer.addHeaderAlias("orderNumber", "订单编号");
			writer.addHeaderAlias("userPhone", "用户账号");
			writer.addHeaderAlias("activityName", "产品名称");
			writer.addHeaderAlias("payAmount", "支付金额");
			writer.addHeaderAlias("payStatusShow", "支付状态");
			writer.addHeaderAlias("payTimeShow", "支付时间");

			// 一次性写出内容，使用默认样式，强制输出标题
			writer.write(bean, true);
			response.setContentType("application/vnd.ms-excel;charset=utf-8");
			//这里容易乱码 处理
			String name = "订单列表";
			name = URLEncoder.encode(name, "UTF-8");
			response.setHeader("Content-Disposition", "attachment;filename=" + name + ".xls");
			out = response.getOutputStream();
			writer.flush(out, true);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 关闭writer
			writer.close();
		}
		//关闭输出Servlet流
		IoUtil.close(out);

	}

	@Override
	public R<ActivityOrderVo> getOrderById(Page page, Long id, String longitude, String latitude) {

		CnActivityOrder order = this.getById(id);
		if (null==order) return R.failed("订单不存在");
		ActivityOrderVo vo = new ActivityOrderVo();
		vo.setOrder(order);

		R<IPage<AboutShopVo>> pageR = cnActivityPersonShopService.getCnActivityOrderShopPage(page, id, longitude, latitude);
		if (CommonConstants.SUCCESS == pageR.getCode()) {
			IPage<AboutActivityShopVo> actPage = new Page<>();
			BeanUtil.copyProperties(pageR.getData(), actPage);
			vo.setPage(actPage);
		}
		return R.ok(vo);

	}

	@Override
	@Transactional
	public R callback(String data, HttpServletRequest request) {

		try {
			log.info("->对返回的数据进行验签");

			log.info("->验签成功，解析回调数据：{}",data);
			JSONObject jsonData =JSONUtil.parseObj(data);
			JSONObject resource = jsonData.getJSONObject("resource");
			String nonce = resource.getStr("nonce");//加密使用的随机串示例值：fdasflkja484w
			String ciphertext = resource.getStr("ciphertext");//Base64编码后的开启/停用结果数据密文示例值：sadsadsadsad
			String associated_data = resource.getStr("associated_data");//附加数据示例值：fdasfwqewlkja484w
//			String algorithm = resource.getStr("algorithm");//对开启结果数据进行加密的加密算法，目前只支持AEAD_AES_256_GCM

			//解密回调信息
			byte[] key = wxPayProperties.getApiv3privateKey().getBytes("UTF-8");
			WxAPIV3AesUtil aesUtil = new WxAPIV3AesUtil(key);
			String ciphert = aesUtil.decryptToString(associated_data.getBytes("UTF-8"),nonce.getBytes("UTF-8"),ciphertext);

			log.info("->解密后的数据：{}",ciphert);
			//解析返回的数据
			JSONObject ciphertJ = JSONUtil.parseObj(ciphert);
//			String appid = ciphertJ.getStr("appid");//直连商户申请的公众号或移动应用appid
//			String mchid = ciphertJ.getStr("mchid");//商户的商户号，由微信支付生成并下发。示例值：1230000109
			String out_trade_no = ciphertJ.getStr("out_trade_no");//商户订单号
			String transaction_id = ciphertJ.getStr("transaction_id");//微信支付订单号
//			String trade_type = ciphertJ.getStr("trade_type");//交易类型
			String trade_state = ciphertJ.getStr("trade_state");//交易状态:交易状态，枚举值：SUCCESS：支付成功 REFUND：转入退款 NOTPAY：未支付 CLOSED：已关闭 REVOKED：已撤销（付款码支付）USERPAYING：用户支付中（付款码支付）PAYERROR：支付失败(其他原因，如银行返回失败)
			String trade_state_desc = ciphertJ.getStr("trade_state_desc");//交易状态描述
			log.info("支付状态：{}",trade_state);
			log.info("支付状态描述：{}",trade_state_desc);
			if(!"SUCCESS".equals(trade_state)) {
				return R.failed();
			}

//			String bank_type = ciphertJ.getStr("bank_type");//付款银行
//			String attach_orderId = ciphertJ.getStr("attach");//附加数据(订单id)
			String success_time = ciphertJ.getStr("success_time");//支付完成时间
			JSONObject payer= ciphertJ.getJSONObject("payer");//支付者
//			String openid = payer.getStr("openid");
			JSONObject amount= ciphertJ.getJSONObject("amount");//订单金额
//			int total = amount.getInt("total");//总金额
			int payer_total = amount.getInt("payer_total");//用户支付金额

			log.info("->处理out_trade_no:{}订单业务{}",out_trade_no,payer_total);

			CnActivityOrder order = this.getOne(Wrappers.lambdaQuery(CnActivityOrder.class).eq(CnActivityOrder::getOrderNumber,out_trade_no));

			if (PayStatusEnum.PAY.getValue()==order.getPayStatus()) return  R.ok();
			int payTotal = order.getPayAmount().multiply(new BigDecimal(100)).intValue();
			if (payTotal!=payer_total){
				log.error("本服务订单金额{}分，微信返回{}分，不一致",payTotal,payer_total);
				return R.failed();
			}
			if (null==order) return R.failed("订单不存在");
			if (PayStatusEnum.PAY.getValue()==order.getPayStatus()
					&&null!=order.getPayAmount()
					&&null!=order.getPayTime()) return  R.ok("支付成功");
			if (PayStatusEnum.BE_OVERDUE.getValue()==order.getPayStatus()) return  R.failed("订单已过期");

			order.setPayStatus(PayStatusEnum.PAY.getValue());//已完成支付
			order.setPayTime(LocalDateTime.parse(success_time,DateTimeFormatter.ISO_DATE_TIME));//支付时间
			String vipAccount = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))+RandomUtil.randomNumbers(4);
			order.setVipAccount(vipAccount);//会员卡号
			order.setVerificationCode((System.currentTimeMillis()+"").substring(5, 13));//核销码
			order.setTransactionId(transaction_id);//微信支付单号
			order.setPayAmount(new BigDecimal(payer_total).divide(new BigDecimal(100)));//支付金额
			order.setPastStatus(0);//会员卡是否有效 0有效 1无效（已过期）
			this.updateById(order);

			//保存用户购买的店铺关联表
			CnActivityPersonShop cnActivityPersonShop = new CnActivityPersonShop();
			cnActivityPersonShop.setUsageTimes(order.getUserConsumptionNumber());
			cnActivityPersonShopService.update(cnActivityPersonShop,Wrappers.lambdaUpdate(CnActivityPersonShop.class).eq(CnActivityPersonShop::getOrderId,order.getId()));

			return R.ok();
		} catch (Exception e) {
			e.printStackTrace();
			return R.failed();
		}
	}


	/**
	 * 取消订单
	 * @param orderId
	 * @return
	 */
	@Override
	public R cancelOrder(Long orderId) {
		return  this.invalidOrder(orderId,PayStatusEnum.BE_CANCEL.getValue());
	}

	/**
	 * 订单过期
	 * @param orderId
	 * @return
	 */
	@Override
	public R expireOrder(Long orderId) {
		return this.invalidOrder(orderId,PayStatusEnum.BE_OVERDUE.getValue());
	}

	/**
	 * 根据订单ID和订单状态更新订单
	 * @param orderId
	 * @param payStatus
	 * @return
	 */
	@Override
	public R invalidOrder(Long orderId,Integer payStatus) {
		try {
			CnActivityOrder order =  this.getById(orderId);
			if(null==order) return R.failed("订单不存在");
			if(PayStatusEnum.PAY.getValue()==order.getPayStatus()) return R.failed("订单已支付，不允许更新状态");
			if(PayStatusEnum.BE_OVERDUE.getValue()==order.getPayStatus()
					||PayStatusEnum.BE_CANCEL.getValue()==order.getPayStatus()) return R.ok("该订单已失效，操作无效");

			CnActivityOrder tempOrder = new CnActivityOrder();
			tempOrder.setId(orderId);
			tempOrder.setPayStatus(payStatus);
			this.updateById(tempOrder);
			return R.ok("取消订单成功");
		} catch (Exception e) {
			e.printStackTrace();
			return R.failed("取消订单失败");
		}
	}


	@Resource
	private WxPayProperties wxPayProperties;

	@Resource
	private RemotePersonService remotePersonService;
	@Override
	public R pay(Long orderId) {
		try {
			log.info("->验证用户");
			R<CnPerson> r = remotePersonService.getPersonById(SecurityUtils.getPersonId());
			if (CommonConstants.SUCCESS!=r.getCode()) return R.failed("用户异常");
			CnPerson person = r.getData();
			if (null==person.getPhone()) return R.failed("用户未授权手机号");
			if (0!=person.getStatus()) return R.failed("用户状态异常");
			CnActivityOrder actOrder = this.getById(orderId);
			//统一下单
			log.info("->统一下单");
			String result = WxPayutil.v3PayTransactionsJsapi(wxPayProperties,person,actOrder);
			JSONObject resultJson = JSONUtil.parseObj(result);
			String prepay_id = resultJson.getStr("prepay_id");
			if(prepay_id==null||"null".equals(prepay_id)) return R.failed("下单失败");
			log.info("->构建小程序调起支付的参数");
			JSONObject data = WxPayutil.toSign(wxPayProperties,prepay_id);
			return R.ok(data,"下单成功");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return R.failed("下单失败");
		}

	}



}
